﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Genetic;
using System.Threading;
using System.IO;

  namespace CPF.StandardTetris
   {
        public partial class STGame
        {

            Population population;
            // Basic Game Management
            private object _semaphore = new object();
            private int _generations_count = 40;
            private int _generation_number = 0;
            private long _previous_game_score = 0;
            private double _generation_average = 0;

            public string BestChromosome
            {
                get
                {
                    return (population == null || population.BestChromosome == null) ? "-1" : population.BestChromosome.ToString();
                }
            }
            public string GenerationNumber
            {
                get
                {
                    return _generation_number.ToString();
                }
            }
            public string GenerationAverage
            {
                get
                {
                    return _generation_average.ToString("G");
                }
            }
            private void saveBestChromosomToFile()
            {
               
                string path = @"best_chromosome.txt";
                // This text is added only once to the file.
                if (!File.Exists(path))
                {
                    // Create a file to write to.
                    using (StreamWriter sw = File.CreateText(path))
                    {
                        sw.WriteLine(BestChromosome);
                    }
                    return;
                }

                using (StreamWriter sw = File.AppendText(path))
                {
                    sw.WriteLine(BestChromosome);
                }    
            }
            private void saveGameState(int call_nr,long score, long number_of_rows_cleared, double time)
            {

                string path = @"game_state.txt";
                // This text is added only once to the file.
                if (!File.Exists(path))
                {
                    // Create a file to write to.
                    using (StreamWriter sw = File.CreateText(path))
                    {

                        sw.WriteLine("Game,Score, Number of rows cleared, Time");
                        sw.WriteLine(call_nr.ToString()+","+score.ToString() + "," + number_of_rows_cleared.ToString() + "," + time.ToString());
                    }
                    return;
                }

                using (StreamWriter sw = File.AppendText(path))
                {
                    sw.WriteLine(call_nr.ToString() + "," + score + "," + number_of_rows_cleared + "," + time);
                }
            }


            private void GeneticAlgorithmOneFitnessFunction()
            {
                population = new Population(5,
                    new TetrisChromosome(6),
                    new TetrisFitnessDelegateFunction(new fitnessCallback(this.fitnessNumberOfRowsCleared)),
                   new RouletteWheelSelection()
                   );

                for (; _generation_number < _generations_count; _generation_number++)
                {

                    // run one epoch of genetic algorithm
                    population.RunEpoch();
                    _generation_average = population.FitnessAvg;
                    // display current path
                    ushort[] bestValue = ((PermutationChromosome)population.BestChromosome).Value;
                    saveBestChromosomToFile();
                }
            }
            
            private int _first_function_count = 17;
            private int _second_function_count = 50;

            private void GeneticAlgorithmTwoFitnessFunction()
            {
                population = new Population(40,
                    new TetrisChromosome(6),
                    new TetrisFitnessDelegateFunction(new fitnessCallback(this.fitnessNumberOfRowsCleared)),
                   new RouletteWheelSelection()
                   );



                for (; _generation_number < _first_function_count; _generation_number++)
                {

                    // run one epoch of genetic algorithm
                    population.RunEpoch();
                    _generation_average = population.FitnessAvg;
                    // display current path
                    ushort[] bestValue = ((PermutationChromosome)population.BestChromosome).Value;
                    saveBestChromosomToFile();
                }
                population = new Population(population,  new TetrisFitnessDelegateFunction(new fitnessCallback(this.fitnessRiskyPlayer)));
                for (; _generation_number < _second_function_count; _generation_number++)
                {

                    // run one epoch of genetic algorithm
                    population.RunEpoch();
                    _generation_average = population.FitnessAvg;
                    // display current path
                    ushort[] bestValue = ((PermutationChromosome)population.BestChromosome).Value;
                    saveBestChromosomToFile();
                }
            }

            public double fitnessNumberOfRowsCleared(TetrisChromosome tCh)
            {
                constatnts = tCh.Value;
                _constants_loaded = true;
                lock (_semaphore)
                    Monitor.Wait(_semaphore);
                return _previous_game_score;
            }
            private int _scores_count = 100;
            public double fitnessConstantPlayer(TetrisChromosome tCh)
            {
                List<long> scores = new List<long>();

                for (int i = 0; i < _scores_count; i++)
                {
                    constatnts = tCh.Value;
                    _constants_loaded = true;
                    lock (_semaphore)
                        Monitor.Wait(_semaphore);
                    scores.Add(_previous_game_score);
                }
                double avg = scores.Average();
             
                double std_dev = calculateStdDev(scores);
                double fitness_value =  (avg * avg) / std_dev;
                return fitness_value;
            }
            public double fitnessRiskyPlayer(TetrisChromosome tCh)
            {
                List<long> scores = new List<long>();

                for (int i = 0; i < _scores_count; i++)
                {
                    constatnts = tCh.Value;
                    _constants_loaded = true;
                    lock (_semaphore)
                        Monitor.Wait(_semaphore);
                    scores.Add(_previous_game_score);
                }
                double avg = scores.Average();

                double std_dev = calculateStdDev(scores);
                double fitness_value =  avg * std_dev;
                return fitness_value;
            }



            private double calculateStdDev(IEnumerable<long> values)
            {
                double ret = 0;
                if (values.Count() > 0)
                {
                    //Compute the Average      
                    double avg = values.Average();
                    //Perform the Sum of (value-avg)_2_2      
                    double sum = values.Sum(d => Math.Pow(d - avg, 2));
                    //Put it all together      
                    ret = Math.Sqrt((sum) / (values.Count() - 1));
                }
                if (ret == 0)
                    ret = 0.001;
                return ret;
            }


            int _call_nr = 0;
            bool _constants_loaded = false;
            bool _GAmode = false;
          

            public void InputEventReset()
            {
                if (_GAmode)
                {
                    if (true == this.mGameState.mOutputToRS232)
                        STRS232.MomentaryRelay_RESET();

                    if (workerThread == null)
                    {
                        workerThread = new Thread(new ThreadStart(GeneticAlgorithmOneFitnessFunction));
                        workerThread.Start();
                    }

                    //constatnts = new ushort[] { 1, 2, 3, 4,5,6 };
                    if (_call_nr == 1)
                    {
                        _previous_game_score = mGameState.mCompletedRows;

                        lock (_semaphore)
                            Monitor.Pulse(_semaphore);
                        _call_nr = 0;
                    }
                    while (_constants_loaded == false)
                        Thread.Sleep(90);

                    //result = MessageBox.Show(message, caption, buttons);  
                    this.mGameState.mPaused = true;
                    this.PrivateGameReset();
                    this.mGameState.mPaused = false;


                    _constants_loaded = false;
                    _call_nr++;
                }
                else
                {
                    if (_call_nr <= _scores_count)
                    {
                        this.mGameState.mPaused = true;
                        
                        //rating = ((-1.0) * Constatnts[0] * (double)pileHeight) + (-1.0) * Constatnts[1] * holes + (pileHeight * bottomTouch) * Constatnts[2] + wallTouch * Constatnts[3] + blockTouch * Constatnts[4] + clears * Constatnts[5];
                        //intuitive
                        //constatnts = new ushort[] { 70, 150, 50, 50, 80, 120 };
                        //constant
                        //constatnts = new ushort[] {104, 240, 183, 183, 139, 11};
                        //risky
                        //constatnts = new ushort[] {67, 146 ,172, 85 ,74, 7};
                        //most rows
                        //constatnts = new ushort[] {254, 192, 119, 119 ,80, 28};
                        if (_call_nr != 0)
                            saveGameState(_call_nr,this.mGameState.mScore, this.mGameState.mCompletedRows, this.mGameState.mTotalElapsedTimeSeconds);
                        _call_nr++;
                        this.PrivateGameReset();
                        this.mGameState.mPaused = false;
                    }
                }
            }
        }
    }

